﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Text.RegularExpressions;
using System.Threading;
using HtmlParsingLibrary;
using HttpLibrary;
using StockTrader.Core;
using StockTrader.Platform.Logging;
using StockTrader.Utilities;

namespace StockTrader.Broker.IciciDirect
{
    public partial class IciciDirectBroker
    {
        CookieContainer mCookieContainer = new CookieContainer();

        bool _fatalNoTryLogin = false;

        // lock on login refresh time  
        object lockLoginRefreshTime = new object();
        // lock on login method  
        object lockLoginMethod = new object();
        private DateTime mLastLoginRefreshTime;
        // Account related values
        string FML_ACCOUNT_value = "dummy";
        string FML_ORD_TRD_DT_BSE_value;
        string FML_ORD_TRD_DT_NSE_value;
        string FML_TRADING_LIMIT_NSE_value;
        string FML_TRADING_LIMIT_BSE_value;
        string DPID, CLIENTID, ACCOUNTNUM;
        string FML_SESSION_ID;
        string m_PipeId_value;
        string FFO_ORD_TRD_DT_value;
        string FFO_GOOD_TILL_DATE_TEXT_value;
        string FFO_GOOD_TILL_DATE1_value;
        string FFO_GOOD_TILL_DATE_value;
        string FFO_GOOD_TILL_DATE_TEMP_value;
        string date2_value;
        string date7_value;

        string mUsername, mPassword, mDOB;

        string _noLoginNonNullResponse = "<script language=\"javascript\"> //if (EscK == \"\") //  EscK = ' or Esc Key'; var SelectedTradingFolder;" +
	"</script> <script language=\"javascript\"> var chttps chttps = \"https://secure.icicidirect.com/NewSiteTrading\";" +
	"location.href=chttps + \"/customer/logon.asp\"</script>";




        // Important urls
        const string URL_ICICI_LOGON = @"https://secure.icicidirect.com/newsitetrading/customer/validlogon.asp?nFile=tradehome";
        const string URL_ICICI_LOGON_OLD = @"https://secure.icicidirect.com/newsitetrading/customer/logon.asp";
        const string URL_ICICI_LOOUT = @"https://secure.icicidirect.com/newsitetrading/customer/logout.asp";
        const string URL_ICICI_MODIFYALLOCATION = @"https://secure.icicidirect.com/NewSiteTrading/trading/equity/includes/trading_modify_alloc.asp";

        const string URL_ICICI_FNO_STOCKLIST = @"https://secure.icicidirect.com/NewSiteTrading/Trading/fno/Includes/StockList.asp";

        // EQUITY
        const string URL_ICICI_EQT_QUOTE = @"http://getquote.icicidirect.com/NewSiteTrading/trading/equity/includes/trading_stock_quote.asp?Symbol=";
        const string URL_ICICI_EQT_SPREAD = @"http://getquote.icicidirect.com/NewSiteTrading/trading/equity/includes/trading_stock_bestbid.asp?Symbol=";//RELIND";
        const string URL_ICICI_EQT_HOME = @"https://secure.icicidirect.com/newsitetrading/Trading/Equity/Trading.asp";
        const string URL_ICICI_EQT_MDP = @"https://secure.icicidirect.com/NewSiteTrading/trading/equity/trading_SelectMDP.asp";
        const string URL_ICICI_EQT_FBS = @"https://secure.icicidirect.com/newsitetrading/trading/equity/includes/Trading_FBS.asp";
        const string URL_ICICI_EQT_SELL = @"https://secure.icicidirect.com/NewSiteTrading/Trading/Equity/Includes/trading_sell.asp";
        const string URL_ICICI_EQT_BUY = @"https://secure.icicidirect.com/NewSiteTrading/Trading/Equity/Includes/trading_buy.asp";
        const string URL_ICICI_EQT_CASHMARGIN_ORDER = @"https://secure.icicidirect.com/NewSiteTrading/Trading/equity/Includes/Trading_verifyOrder.asp";
        const string URL_ICICI_EQT_MARGINPLUS_ORDER = @"https://secure.icicidirect.com/NewSiteTrading/Trading/Equity/Includes/Trading_SLTP_VerifyOrder.asp";
        const string URL_ICICI_EQT_CANCEL_ORDER = @"https://secure.icicidirect.com/NewSiteTrading/Trading/equity/Includes/trading_cancel_order.asp";
        const string URL_ICICI_EQT_ORDERBOOK = @"https://secure.icicidirect.com/NewSiteTrading/Trading/equity/Includes/trading_order_book.asp";
        const string URL_ICICI_EQT_TRADEBOOK = @"https://secure.icicidirect.com/NewSiteTrading/Trading/equity/Includes/trading_trade_book.asp";
        const string URL_ICICI_EQT_FBS_CASHMARGIN_ORDER = @"https://secure.icicidirect.com/NewSiteTrading/Trading/equity/Includes/Trading_FBSubmitOrder.asp";
        //const string URL_ICICI_EQT_BESTBIDASK = @"http://getquote.icicidirect.com/NewSiteTrading/trading/equity/includes/trading_stock_bestbid.asp?Frm=undefined&Symbol=";

        // FNO
        const string URL_ICICI_FNO_QUOTE = @"https://secure.icicidirect.com/NewSiteTrading/Trading/fno/Includes/GetQuote.asp";//?Symbol=BAAUTO
        const string URL_ICICI_FNO_SPREAD = @"https://secure.icicidirect.com/NewSiteTrading/Trading/fno/Includes/Bestbid.asp";//?Page=&FFO_UNDRLYNG=NIFTY%20&FFO_XCHNG_CD=NFO&FFO_PRDCT_TYP=O&FFO_OPT_TYP=C&FFO_EXPRY_DT=26-Apr-2012&FFO_EXER_TYP=E&FFO_RQST_TYP=*&FFO_STRK_PRC=560000&FFO_MIN_LOT_QTY=50";
        const string URL_ICICI_FNO_FBS = @"https://secure.icicidirect.com/newsitetrading/trading/fno/includes/FNO_FBS.asp";
        const string URL_ICICI_FNO_HOME = @"https://secure.icicidirect.com/NewSiteTrading/Trading/fno/fnohome.asp";
        const string URL_ICICI_FNO_VERIFYORDER = @"https://secure.icicidirect.com/NewSiteTrading/trading/fno/includes/VerifyOrder.asp";
        const string URL_ICICI_FNO_ORDERBOOK = @"https://secure.icicidirect.com/NewSiteTrading/trading/fno/includes/OrderBook.asp";
        const string URL_ICICI_FNO_TRADEBOOK = @"https://secure.icicidirect.com/NewSiteTrading/Trading/fno/Includes/Tradebook.asp";
        const string URL_ICICI_FNO_FBS_SUBMITORDER = @"https://secure.icicidirect.com/NewSiteTrading/trading/fno/includes/FNO_FBS_SubmitOrder.asp";
        //const string URL_ICICI_FNO_FBS_REFERER = @"https://secure.icicidirect.com/NewSiteTrading/trading/fno/FNO_FBS.asp";
        const string URL_ICICI_FNO_CANCEL_ORDER = @"https://secure.icicidirect.com/NewSiteTrading/Trading/fno/Includes/CancelOrder.asp";



        HashSet<string> ServerLoginTimeoutRefreshURLs = new HashSet<string>()
        {
            URL_ICICI_LOGON,
            URL_ICICI_MODIFYALLOCATION,

            URL_ICICI_FNO_FBS,
            URL_ICICI_FNO_VERIFYORDER,
            URL_ICICI_FNO_ORDERBOOK,
            URL_ICICI_FNO_TRADEBOOK,
            URL_ICICI_FNO_FBS_SUBMITORDER,
            URL_ICICI_FNO_CANCEL_ORDER,

            URL_ICICI_EQT_MDP,
            URL_ICICI_EQT_FBS,
            URL_ICICI_EQT_SELL,
            URL_ICICI_EQT_BUY,
            URL_ICICI_EQT_CASHMARGIN_ORDER,
            URL_ICICI_EQT_MARGINPLUS_ORDER,
            URL_ICICI_EQT_CANCEL_ORDER,
            URL_ICICI_EQT_ORDERBOOK,
            URL_ICICI_EQT_TRADEBOOK,
            URL_ICICI_EQT_FBS_CASHMARGIN_ORDER
        };


        bool mLoggedIn;

        public IciciDirectBroker(string username,
            string password, string dob)
        {
            mUsername = username;
            mPassword = password;
            mDOB = dob;

            //noLoginNonNullResponse = noLoginNonNullResponse.Replace(" ", "").Replace("\n", "").Replace("\t", "");
            _noLoginNonNullResponse = Regex.Replace(_noLoginNonNullResponse, @"\s", "");
        }
        /////////////////////////////////////////////////////
        //////      ACCOUNT LOGIN FUNCTIONALITY       ///////
        ///////////////////////////////////////////////////// 

        // NOTE: Should be used only when logged-in status is mandatory
        // so that server contact time (which matters to maintain the login) can be updated
        // Do not use this (Use HttpHelper.GetWebPageResponse instaed) for GET QUOTE or similar where log-in is not needed

        private string IciciGetWebPageResponse(string url,
            string postdata,
            string referer,
            CookieContainer cookieContainer,
            out BrokerErrorCode errorCode)
        {
            errorCode = BrokerErrorCode.Success;
            string data = HttpHelper.GetWebPageResponse(url,
                postdata,
                referer,
                cookieContainer);

            // refresh the time if data is valid login and url is of refresh type
            errorCode = CheckResponseDataValidity(data);
            if (ServerLoginTimeoutRefreshURLs.Contains(url) && errorCode.Equals(BrokerErrorCode.Success))
            {
                RefreshLastServerContactTime();
            }
            return data;
        }

        // get important account related values once during login
        BrokerErrorCode GetImportantValues()
        {
            BrokerErrorCode errorCode = BrokerErrorCode.NotLoggedIn;
            string equityFastBuySellPageData = null;
            string equityTradingPageData = IciciGetWebPageResponse(URL_ICICI_EQT_HOME,
                null,
                null,
                mCookieContainer,
                out errorCode);
            equityTradingPageData = IciciGetWebPageResponse(URL_ICICI_EQT_MDP,
                    null,
                    URL_ICICI_EQT_HOME,
                    mCookieContainer,
                    out errorCode);
            if (errorCode.Equals(BrokerErrorCode.Success))
            {
                equityFastBuySellPageData = IciciGetWebPageResponse(URL_ICICI_EQT_FBS,
                    null,
                    URL_ICICI_EQT_HOME,
                    mCookieContainer,
                    out errorCode);
            }

            if (errorCode.Equals(BrokerErrorCode.Success))
            {
                FML_ACCOUNT_value = StringParser.GetStringBetween(equityFastBuySellPageData,
                    0,
                    "<input type=\"hidden\" name=\"FML_ACCOUNT\" value=\"",
                    "\">",
                    null);
                FML_ORD_TRD_DT_BSE_value = StringParser.GetStringBetween(equityFastBuySellPageData,
                    0,
                    "<input type=\"hidden\" name=\"FML_ORD_TRD_DT_BSE\" value=\"",
                    "\">",
                    null);
                FML_ORD_TRD_DT_NSE_value = StringParser.GetStringBetween(equityFastBuySellPageData,
                   0,
                   "<input type=\"hidden\" name=\"FML_ORD_TRD_DT_NSE\" value=\"",
                   "\">",
                   null);
                FML_TRADING_LIMIT_NSE_value = StringParser.GetStringBetween(equityFastBuySellPageData,
                   0,
                   "<input type=\"hidden\" name=\"FML_TRADING_LIMIT_NSE\" value=\"",
                   "\">",
                   null);
                FML_TRADING_LIMIT_BSE_value = StringParser.GetStringBetween(equityFastBuySellPageData,
                   0,
                   "<input type=\"hidden\" name=\"FML_TRADING_LIMIT_BSE\" value=\"",
                   "\">",
                   null);

                string[] parts = FML_ACCOUNT_value.Split(new string[] { "-" }, StringSplitOptions.RemoveEmptyEntries);
                DPID = parts[0];
                CLIENTID = parts[1];
                ACCOUNTNUM = parts[2];

                string fastBuySellDerivativePageData = IciciGetWebPageResponse(URL_ICICI_FNO_FBS,
                    null,
                    URL_ICICI_FNO_HOME,
                    mCookieContainer,
                    out errorCode);

                if (errorCode.Equals(BrokerErrorCode.Success))
                {
                    m_PipeId_value = StringParser.GetStringBetween(fastBuySellDerivativePageData,
                       0,
                       "<input type=\"hidden\" name=\"m_PipeId\" value=\"",
                       "\"",
                       null);
                    FFO_ORD_TRD_DT_value = StringParser.GetStringBetween(fastBuySellDerivativePageData,
                       0,
                       "<input type=\"hidden\" name=\"FFO_ORD_TRD_DT\" value=\"",
                       "\"",
                       null).Replace(" ", "+");
                    FFO_GOOD_TILL_DATE_TEXT_value = StringParser.GetStringBetween(fastBuySellDerivativePageData,
                       0,
                       "<input name=\"FFO_GOOD_TILL_DATE_TEXT\" type=\"hidden\" value=\"",
                       "\"",
                       null).Replace("/", "%2F");
                    FFO_GOOD_TILL_DATE1_value = StringParser.GetStringBetween(fastBuySellDerivativePageData,
                       0,
                       "<input type=\"hidden\" name=\"FFO_GOOD_TILL_DATE1\" value=\"",
                       "\"",
                       null).Replace("/", "%2F");
                    FFO_GOOD_TILL_DATE_value = StringParser.GetStringBetween(fastBuySellDerivativePageData,
                       0,
                       "<input type=\"hidden\" name=\"FFO_GOOD_TILL_DATE\" value=\"",
                       "\"",
                       null).Replace("/", "%2F");
                    FFO_GOOD_TILL_DATE_TEMP_value = StringParser.GetStringBetween(fastBuySellDerivativePageData,
                       0,
                       "<input type=\"hidden\" name=\"FFO_GOOD_TILL_DATE_TEMP\" value=\"",
                       "\"",
                       null).Replace("/", "%2F");
                    date2_value = StringParser.GetStringBetween(fastBuySellDerivativePageData,
                      0,
                      "<input type=\"hidden\" name=\"date2\" value=\"",
                      "\"",
                      null).Replace("/", "%2F");
                    date7_value = StringParser.GetStringBetween(fastBuySellDerivativePageData,
                     0,
                     "<input type=\"hidden\" name=\"date7\" value=\"",
                     "\"",
                     null).Replace("/", "%2F");
                }
            }

            return errorCode;
        }

        private BrokerErrorCode CheckResponseDataValidity(string isLoggedInCheckData)
        {
            BrokerErrorCode errorCode = BrokerErrorCode.Success;
            if (String.IsNullOrEmpty(isLoggedInCheckData))
            {
                errorCode = BrokerErrorCode.NullResponse;
            }
            else if (isLoggedInCheckData.IndexOf("have faced some technical or connectivity") > -1)
                errorCode = BrokerErrorCode.TechnicalReason;
            else if (isLoggedInCheckData.IndexOf("close your browser and try again") > -1)
                errorCode = BrokerErrorCode.TechnicalReason;
            else if ((isLoggedInCheckData.IndexOf("Invalid Login Id", StringComparison.OrdinalIgnoreCase) >= 0) ||
                (isLoggedInCheckData.IndexOf("Timeout", StringComparison.OrdinalIgnoreCase) >= 0) ||
                (isLoggedInCheckData.IndexOf("Bad connection", StringComparison.OrdinalIgnoreCase) >= 0) ||
                (isLoggedInCheckData.IndexOf("login id", StringComparison.OrdinalIgnoreCase) > 0) ||
                (isLoggedInCheckData.IndexOf("Invalid User", StringComparison.OrdinalIgnoreCase) >= 0) ||
                (isLoggedInCheckData.IndexOf(/*"Session has */"Timed Out", StringComparison.OrdinalIgnoreCase) >= 0) ||
                (isLoggedInCheckData.IndexOf("Please Login again", StringComparison.OrdinalIgnoreCase) >= 0) || 
                (isLoggedInCheckData.Length < 2000 && isLoggedInCheckData.IndexOf("location.href=chttps + \"/customer/logon.asp\"", StringComparison.OrdinalIgnoreCase) >= 0)
                )
            {
                mLoggedIn = false;
                errorCode = BrokerErrorCode.NotLoggedIn;
            }
            else
            {
                //resultString = Regex.Replace(subjectString, @"(?:(?:\r?\n)+ +){2,}", @"\n");
                string isLoggedInCheckData1 = Regex.Replace(isLoggedInCheckData, @"\s", "");
                //isLoggedInCheckData = isLoggedInCheckData.Replace(" ", "").Replace("\n", "").Replace("\t", "");
                if (isLoggedInCheckData1 == _noLoginNonNullResponse)
                {
                    errorCode = BrokerErrorCode.NotLoggedIn;
                    mLoggedIn = false;
                }
            }

            if (errorCode != BrokerErrorCode.Success)
            {
                string desc = string.Format("CheckResponseValidity Failed-Code-{0}", errorCode);
                string content = string.Format("<b>{0}\n\n\n\n{1}\n<b>", desc, isLoggedInCheckData);
                File.WriteAllText(SystemUtils.GetErrorFileName(desc, true), content);
            }

            return errorCode;
        }

        // Is logged in
        public BrokerErrorCode IsLoggedIn()
        {
            BrokerErrorCode errorCode = TouchServer();

            if (errorCode.Equals(BrokerErrorCode.Success))
                mLoggedIn = true;
            
            return errorCode;
        }

        // Do login
        public BrokerErrorCode LogIn()
        {
            if (_fatalNoTryLogin)
            {
                FileTracing.TraceOut(Thread.CurrentThread.Name + "ERROR: Not trying Actual LogIn() because now in FatalNoLoginTry mode.");
                return BrokerErrorCode.FatalNoLoginTry;
            }

            BrokerErrorCode errorCode = IsLoggedIn();
            if (!errorCode.Equals(BrokerErrorCode.Success))
            {
                mLoggedIn = false;
                FileTracing.TraceOut(Thread.CurrentThread.Name + ": In Actual LogIn()");

                // re-login
                mCookieContainer = new CookieContainer();
                string loginResponse = HttpHelper.GetWebPageResponse(URL_ICICI_LOGON,
                    "FML_USR_ID=" + mUsername + "&FML_USR_USR_PSSWRD=" + mPassword + "&FML_USR_DT_BRTH=" + mDOB +
                    "&startin=12&Submit=Login+Now",
                    null,
                    mCookieContainer);
                
                if (String.IsNullOrEmpty(loginResponse)) 
                    errorCode = BrokerErrorCode.Http;
                else if ((loginResponse.IndexOf(" logged on", StringComparison.OrdinalIgnoreCase) >= 0) || (IsLoggedIn() == BrokerErrorCode.Success))
                {
                    errorCode = GetImportantValues();
                    if (errorCode.Equals(BrokerErrorCode.Success))
                    {
                        mLoggedIn = true;
                        RefreshLastServerContactTime();
                    }
                }
                else
                {
                    if (loginResponse.IndexOf("customer_change_password", StringComparison.OrdinalIgnoreCase) >= 0) errorCode = BrokerErrorCode.ChangePassword;
                    if (loginResponse.IndexOf("echnical reason", StringComparison.OrdinalIgnoreCase) >= 0) errorCode = BrokerErrorCode.TechnicalReason;
                    else if (loginResponse.IndexOf("Locked", StringComparison.OrdinalIgnoreCase) >= 0)
                    {
                        _fatalNoTryLogin = true;
                        errorCode = BrokerErrorCode.Locked;
                    }
                    else if (loginResponse.IndexOf("Invalid Login Id or Password", StringComparison.OrdinalIgnoreCase) >= 0)
                    {
                        _fatalNoTryLogin = true;
                        errorCode = BrokerErrorCode.InvalidLoginPassword;
                    }
                    else errorCode = BrokerErrorCode.Unknown;
                }
            }

            return errorCode;
        }

        // Check Last Refresh time & if past 3 minutes then login
        // Even if someone else acquired login Session by weblogin etc.
        // Still this should work as the LastLoginRefreshTime becomes stale eventually and 
        // resulting into relogin trigger

        // Keep calling this in a separate thread 
        public BrokerErrorCode CheckAndLogInIfNeeded(bool bForceLogin, bool bCheckRemoteControl = false)
        {
            TimeSpan maxTimeWithoutRefresh = new TimeSpan(0, 3, 0);
            BrokerErrorCode errorCode = TouchServer();

            lock (lockLoginMethod)
            {
                if (bForceLogin || (errorCode != BrokerErrorCode.Success) || ((DateTime.Now - LastLoginRefreshTime) > maxTimeWithoutRefresh))
                {
                    ProgramRemoteControl remoteControlValue = ProgramRemoteControl.RUN;

                    if (bCheckRemoteControl)
                    {
                        remoteControlValue = RemoteUtils.GetProgramRemoteControlValue();
                    }
                    // keep looping until PAUSE status is reset
                    if (remoteControlValue.Equals(ProgramRemoteControl.PAUSE) ||
                        remoteControlValue.Equals(ProgramRemoteControl.STOP) ||
                        remoteControlValue.Equals(ProgramRemoteControl.HIBERNATE))
                    {
                        string traceString = string.Format("CheckAndLogInIfNeeded: Remote Control {0} the LOGIN\n", remoteControlValue.ToString());
                        FileTracing.TraceOut(traceString);
                        errorCode = BrokerErrorCode.RemotePausedOrStopped;
                    }
                    else
                    {
                        string traceString = string.Format("CheckAndLogInIfNeeded: Need to do actual LogIn. ");
                        errorCode = LogIn();
                        traceString += "LogIn() returned: " + errorCode.ToString();
                        FileTracing.TraceOut(traceString);
                    }
                }
            }
            return errorCode;
        }


        public BrokerErrorCode TouchServer()
        {
            BrokerErrorCode errorCode = BrokerErrorCode.Success;

            string data = IciciGetWebPageResponse(URL_ICICI_MODIFYALLOCATION,
                null,
                URL_ICICI_EQT_HOME,
                mCookieContainer,
                out errorCode);

            string ebaAccountWrap1 = "<td width=\"16%\" align=\"left\">";
            string ebaAccountWrap2 = "<input type=\"hidden\" name = \"eba_acc\"";
							

            //string accountWrap = "<input type=\"hidden\" name=\"FML_ACCOUNT\" value=\"";
            string account = "";


            if (errorCode.Equals(BrokerErrorCode.Success) && data.Contains(ebaAccountWrap2))
            {
                //account = StringParser.GetStringBetween(data,
                //    0,
                //    "<input type=\"hidden\" name=\"FML_ACCOUNT\" value=\"",
                //    "\">",
                //    null);

                account = StringParser.GetStringBetween(data,
                    0,
                    ebaAccountWrap1,
                    ebaAccountWrap2,
                    null).Trim();
            }

            if (account == ACCOUNTNUM)
                return BrokerErrorCode.Success;
            else return BrokerErrorCode.NotLoggedIn;
        }

        private DateTime LastLoginRefreshTime
        {
            get
            {
                lock (lockLoginRefreshTime)
                {
                    return mLastLoginRefreshTime;
                }
            }
            set
            {
                lock (lockLoginRefreshTime)
                {
                    mLastLoginRefreshTime = value;
                }
            }
        }

        private void RefreshLastServerContactTime()
        {
            LastLoginRefreshTime = DateTime.Now;
        }
        public void LogOut()
        {
            mLoggedIn = false;
            FileTracing.TraceOut("LogOut");
            HttpHelper.GetWebPageResponse(URL_ICICI_LOOUT,
                null,
                null,
                mCookieContainer);
            LastLoginRefreshTime = DateTime.MinValue;
        }


        #region IDisposable Members
        void IDisposable.Dispose()
        {
            LogOut();
            ClearTradeReferenceNumbers(TradingSectionType.DERIVATIVES);
            ClearTradeReferenceNumbers(TradingSectionType.EQUITY);
            ClearOrderReferenceNumbers(TradingSectionType.EQUITY);
        }


        #endregion
    }
}